﻿using ArtMath.Core.Data;
using System;

namespace ArtMath.Core.Helper
{
    /// <summary>
    /// 曲线切线
    /// </summary>
    public static class CurveTangent
    {
        public static PointD[] GetCircleTangentPoints(PointD ct, double r, PointD pt)
        {
            return GetArcTangentPoints(ct, r, 0, Angle.RadRound, pt);
        }
        public static PointD[] GetArcTangentPoints(PointD ct, double r, double start, double sweep, PointD pt)
        {
            return CurveIntersection.ArcsIntersection(ct, r, start, sweep, (ct + pt) / 2, ct.DistanceTo(pt) / 2, 0, Angle.RadRound);
        }
        //public static Tuple<PointD, PointD>[] GetCirclesCommonTangentLines(PointD ct1, double r1, double start1, double sweep1, PointD ct2, double r2, double start2, double sweep2)
        //{
        //    return null;
        //}
        public static PointD[] GetEllipseTangentPoints(PointD ct, double a, double b, double angle, PointD pt, PointD? focus1 = null, PointD? focus2 = null)
        {
            if (!focus1.HasValue)
            {
                focus1 = ct.Offset(angle + Math.PI, Math.Sqrt(a * a - b * b));
            }
            if (!focus2.HasValue)
            {
                focus2 = ct.Offset(angle, Math.Sqrt(a * a - b * b));
            }
            double delta = pt.DistanceTo(focus1.Value) + pt.DistanceTo(focus2.Value) - 2 * a;
            if (delta > 0)
            {
                pt = (pt - ct).Rotate(-angle);
                double sqrt = 2 * Math.Sqrt(a * a * pt.Y * pt.Y * pt.X * pt.X - pt.X * pt.X * b * b * a * a + pt.X * pt.X * pt.X * pt.X * b * b),
                    anmb = a * a * pt.Y * pt.Y + pt.X * pt.X * b * b;
                PointD p1 = new PointD(-a * a * (0.5 * pt.Y / anmb * (2 * pt.Y * a * a + sqrt) - 1) / pt.X,
                    0.5 / anmb * (2 * pt.Y * a * a + sqrt) * b * b),
                    p2 = new PointD(-a * a * (0.5 * pt.Y / anmb * (2 * pt.Y * a * a - sqrt) - 1) / pt.X,
                    0.5 / anmb * (2 * pt.Y * a * a - sqrt) * b * b);
                return new PointD[] { p1.Rotate(angle) + ct, p2.Rotate(angle) + ct };
            }
            else if (delta == 0)
            {
                return new PointD[] { pt };
            }
            else
            {
                return null;
            }
        }
        public static PointD[] GetHyperbolaTangentPoints(PointD ct, double a, double b, double angle, PointD pt, PointD? focus1 = null, PointD? focus2 = null)
        {
            if (!focus1.HasValue)
            {
                focus1 = ct.Offset(angle + Math.PI, Math.Sqrt(a * a + b * b));
            }
            if (!focus2.HasValue)
            {
                focus2 = ct.Offset(angle, Math.Sqrt(a * a + b * b));
            }
            double delta = Math.Abs(2 * a) - Math.Abs(pt.DistanceTo(focus1.Value) - pt.DistanceTo(focus2.Value));
            if (delta > 0)
            {
                pt = (pt - ct).Rotate(-angle);
                double sqrt = 2 * Math.Sqrt(pt.Y * pt.Y * pt.Y * pt.Y * a * a + pt.Y * pt.Y * a * a * b * b - pt.X * pt.X * b * b * pt.Y * pt.Y),
                    namb = a * a * pt.Y * pt.Y - pt.X * pt.X * b * b;
                PointD p1 = new PointD(0.5 / namb * (-2 * pt.X * b * b + sqrt) * a * a,
                    b * b * (0.5 * pt.X / namb * (-2 * pt.X * b * b + sqrt) - 1) / pt.Y),
                    p2 = new PointD(0.5 / namb * (-2 * pt.X * b * b - sqrt) * a * a,
                    b * b * (0.5 * pt.X / namb * (-2 * pt.X * b * b - sqrt) - 1) / pt.Y);
                return new PointD[] { p1.Rotate(angle) + ct, p2.Rotate(angle) + ct };
            }
            else if (delta == 0)
            {
                return new PointD[] { pt };
            }
            else
            {
                return null;
            }
        }
        public static PointD[] GetParabolaTangentPoints(PointD ct, double p, double angle, PointD pt, PointD? focus = null)
        {
            if (!focus.HasValue)
            {
                focus = ct.Offset(angle, p / 2);
            }
            double[] abc = (2 * ct - focus.Value).GetABC(-1 / ct.GetSlope(focus.Value));
            double delta = pt.DistanceTo(focus.Value) - pt.DistanceTo(abc[0], abc[1], abc[2]);
            if (delta > 0)
            {
                pt = (pt - ct).Rotate(-angle);
                double sqrt = Math.Sqrt(pt.Y * pt.Y - 2 * p * pt.X);
                PointD p1 = new PointD((pt.Y * (pt.Y + sqrt) - p * pt.X) / p, pt.Y + sqrt),
                    p2 = new PointD((pt.Y * (pt.Y - sqrt) - p * pt.X) / p, pt.Y - sqrt);
                return new PointD[] { p1.Rotate(angle) + ct, p2.Rotate(angle) + ct };
            }
            else if (delta == 0)
            {
                return new PointD[] { pt };
            }
            else
            {
                return null;
            }
        }
    }
}
